/**
 * Copyright 2012 Pablo Danilo Aliscioni
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package ar.com.pabloaliscioni.cloud.core.storage.providers.rackspace;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.apache.http.HttpException;

import ar.com.pabloaliscioni.cloud.api.exceptions.ConfigurationException;
import ar.com.pabloaliscioni.cloud.api.exceptions.ServiceException;
import ar.com.pabloaliscioni.cloud.core.configuration.ProviderConfiguration;
import ar.com.pabloaliscioni.cloud.core.storage.BaseStorageClient;

import com.rackspacecloud.client.cloudfiles.FilesClient;
import com.rackspacecloud.client.cloudfiles.FilesContainer;
import com.rackspacecloud.client.cloudfiles.FilesException;
import com.rackspacecloud.client.cloudfiles.FilesObject;
import com.rackspacecloud.client.cloudfiles.FilesObjectMetaData;

/**
 * Implementation of the IStorageClient interface for Rackspace CloudFiles Storage service
 * 
 * @author Pablo Danilo Aliscioni
 * @since 1.0
 */
public class RackspaceCloudFilesStorageClient extends BaseStorageClient
{
	private String username;
	private String accessKey;
	private String authenticationURL;
	private int connectionTimeout;
	
	private FilesClient serviceClient;

	@Override
	public void configure(ProviderConfiguration providerConfiguration)
	{
		try
		{
			this.setProviderName(providerConfiguration.getName());
			
			username = providerConfiguration.getConfigurationValue("Username");
			accessKey = providerConfiguration.getConfigurationValue("Access-Key");
			authenticationURL = providerConfiguration.getConfigurationValue("Authentication-URL");
			connectionTimeout = Integer.parseInt(providerConfiguration.getConfigurationValue("Connetion-Timeout"));
		}
		catch (Exception e)
		{
			throw new ConfigurationException(e);
		}
	}
	
	private FilesClient getServiceClient()
	{
		try
		{
			if(serviceClient == null)
			{
				serviceClient = new FilesClient(username, accessKey);
				
				serviceClient.setAuthenticationURL(authenticationURL);
				serviceClient.setConnectionTimeOut(connectionTimeout);
			}

			if(!serviceClient.isLoggedin())
			{
				serviceClient.login();
			}
			
			return serviceClient;
		}
		catch (Exception e)
		{
			throw new ConfigurationException(e);
		}
	}
	
	// API Implementation
	
	@Override
	public boolean containerExists(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			return getServiceClient().containerExists(URLEncoder.encode(containerName,"UTF-8"));
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void createContainer(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			getServiceClient().createContainer(URLEncoder.encode(containerName,"UTF-8"));
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void deleteContainer(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			getServiceClient().deleteContainer(URLEncoder.encode(containerName,"UTF-8"));
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void deleteFile(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			getServiceClient().deleteObject(URLEncoder.encode(containerName,"UTF-8"), URLEncoder.encode(fileName,"UTF-8"));
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public boolean fileExists(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			String encodedFileName = URLEncoder.encode(fileName,"UTF-8");
			
			for (FilesObject item : getServiceClient().listObjects(URLEncoder.encode(containerName,"UTF-8")))
			{
				if(item.getName().equals(encodedFileName))
					return true;
			}
			
			return false;
		}
		catch (FilesException | IOException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public byte[] getFileContent(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			return getServiceClient().getObject(URLEncoder.encode(containerName,"UTF-8"), URLEncoder.encode(fileName,"UTF-8"));
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public String getFileContentType(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			FilesObjectMetaData objectMetadata = getServiceClient()
					.getObjectMetaData(URLEncoder.encode(containerName,"UTF-8"), URLEncoder.encode(fileName,"UTF-8"));
			
			return objectMetadata.getMimeType();
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public Set<String> listContainers()
	{
		try
		{
			Set<String> containers = new HashSet<String>();
			
			for (FilesContainer item : getServiceClient().listContainers())
			{
				containers.add(item.getName());
			}
			
			return containers;
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public Set<String> listFiles(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			Set<String> files = new HashSet<String>();
			
			for (FilesObject item : getServiceClient().listObjects(URLEncoder.encode(containerName,"UTF-8")))
			{
				files.add(item.getName());
			}
			
			return files;
		}
		catch (FilesException | IOException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void saveFile(String containerName, String fileName, byte[] fileContent)
	{
		saveFile(containerName, fileName, fileContent, "application/octet-stream");
	}
	
	@Override
	public void saveFile(String containerName, String fileName, byte[] fileContent, String contentType)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		if(fileContent == null)
			throw new IllegalArgumentException("Invalid file content");
		
		if(contentType == null || contentType.equals("") || !contentType.contains("/"))
			throw new IllegalArgumentException("Invalid content type");
		
		try
		{
			getServiceClient().storeObject(URLEncoder.encode(containerName,"UTF-8"), fileContent, 
					contentType, URLEncoder.encode(fileName,"UTF-8"), new HashMap<String, String>());
		}
		catch (IOException | HttpException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public boolean isValidContainerName(String containerName)
	{
		if (containerName == null || containerName.length() == 0 || containerName.length() > 256 || containerName.contains("/"))
			return false;
		
		return true;
	}
    
	@Override
	public boolean isValidFileName(String fileNname)
	{
		if (fileNname == null || fileNname.length() == 0 || fileNname.length() > 1024)
			return false;
		
		return true;
	}
}
